import React, {
  ForwardedRef,
  forwardRef,
  HTMLAttributes,
  useCallback,
  useContext,
  useEffect,
  useImperativeHandle,
  useMemo,
  useRef,
  useState,
} from 'react';
import { Button, ConfigProvider, Table } from 'antd';
import { getMessage, TextWidget } from '@inbiz/react';
import { GlobalRegistry } from '@inbiz/core';
import { Field } from '@formily/core';
import { customRequest } from '@inbiz/hooks';
import {
  SortableContainer,
  SortableContainerProps,
  SortableElement,
  SortableHandle,
} from 'react-sortable-hoc';
import ResizeObserver from 'rc-resize-observer';
import ResizableTitle from './ResizableTitle';
import { arrayMoveImmutable } from 'array-move';
import cls from 'classnames';
import { MenuOutlined, RightOutlined, SyncOutlined } from '@ant-design/icons';
import {
  ColumnType,
  FilterValue,
  SorterResult,
  TableCurrentDataSource,
  TablePaginationConfig,
  TableRowSelection,
} from 'antd/lib/table/interface';
import PrintTable from './PrintTable';
import TableModal from './TableModal';
import TableTop from './TableTop';
import Search from './Search';
import { ExportAction, ImportAction } from './Actions';
import api from './api';
import {
  PageData,
  QueryParam,
  RecordType,
  TableCtxType,
  TablePreviewProps,
} from '../../interfaces';
import { arrIsEmpty, ColumnConst, EventsMap, objIsNotEmpty, Option, UnitEnum } from '../../shared';
import {
  useActionReducer,
  useColumnConfig,
  useColumns,
  useColumnsWidth,
  useFetchData,
  useFormatColumns,
  useFormItems,
  useInbizPrefixCls,
  useInitSearchRef,
  usePagination,
  usePrint,
  useResize,
  useScrollX,
  percentageWidthCount,
  useGetChild,
  useToolbar,
  useRefreshData,
  setTableStorage,
  useSearchTagsData,
} from '../../hooks';
import { InBizTableCtx, InBizTableRefCtx, TableCtx } from '../../context';
import formatters from '../../formatters';
import { getHeights, setTableBodyHeight } from '../../util';
import tableApi from './serviceApi';
import { commonLang } from '@inbiz/utils';

const SortableItem = SortableElement((props: HTMLAttributes<any>) => <tr {...props} />) as any;
const SortableBody = SortableContainer((props: HTMLAttributes<any>) => <tbody {...props} />) as any;

const InBizTable = (props: TablePreviewProps, ref: ForwardedRef<any>) => {
  const {
    rowSelection,
    autoLoad,
    showPage,
    pageRefresh = true,
    zebraEffect,
    controlId = '',
    inbiz,
    selectComponent,
    height,
    bordered = undefined,
    search,
    dragSort,
    showSortIcon = true,
    sortField,
    sortInterface,
    showPosition = 'right',
    primarykey,
    showHeightRule,
    boxPadding = 0,
    boxMargin = 0,
    pressDelay,
    emit,
    title,
    viewMode,
    pagination,
    selectBackColor = true,
    ...rest
  } = useContext(InBizTableCtx);
  const { attrs, queryFields } = useColumnConfig();
  const toolbar = useToolbar();
  const formItems = useFormItems();
  const { eventsRef, searchRef, modalFormRef, inbizTableRef } = useContext(InBizTableRefCtx);
  const tablePrefixCls = useInbizPrefixCls('table-show-antd-empty');
  const columns = useFormatColumns();
  const [queryParam, setQueryParam] = useState<QueryParam>({
    current: 1,
    pageSize: formItems.pageSize || 30,
  });
  const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
  const [resizeColumn, setResizeColumn] = useState<boolean>(false);
  const [attrFilters, setAttrFilters] = useState<any>({});
  const [clickRow, setClickRow] = useState<string>('');
  const [tableHeight, setTableHeight] = useState<number>();
  const [nextBtn, setNextBtn] = useState<boolean>(true);
  const [tableWidth, setTableWidth] = useState<number>(document.body.clientWidth);
  const [querySortField, setQuerySortField] = useState<{ fieldName: string; sortType: string; } | undefined>(undefined);
  const { printTableRef, printVisible, handlePrint } = usePrint();
  const { loading, fetchData, pageData, setPageData } = useFetchData();
  const { tagData, fetchTagData } = useSearchTagsData();
  const refreshData = useRefreshData(fetchData);
  useInitSearchRef();
  const primaryKey = useMemo(() => {
    return primarykey ? primarykey : pageData.primary ?? 'id';
  }, [pageData, primarykey]);

  const relationField = useMemo(() => {
    return pageData.relationField;
  }, [pageData]);

  const models = useMemo(() => {
    return formatters.getModels(queryFields);
  }, [queryFields]);
  const childRef = useGetChild();
  // tableCacheRef 表格关联组件缓存
  const tableCacheRef = useRef<any>({ data: pageData.data });
  // tableRef 表格组件唯一标识，用于获取原生属性
  const tableRef = useRef<HTMLDivElement>(null);
  const newRowSelection: TableRowSelection<RecordType> | undefined = useMemo(() => {
    return rowSelection
      ? {
        selectedRowKeys,
        columnWidth: 50,
        getCheckboxProps: eventsRef.current?.setCheckboxProps,
        fixed: formItems.widthUnit === UnitEnum.pixel,
        onSelect: (record: RecordType, selected: boolean, selectedRows: RecordType[]) => {
          (emit as any)?.(EventsMap.onSelect, record.id, selected, selectedRows);
        },
        onSelectAll: (
          selected: boolean,
          selectedRows: RecordType[],
          changeRows: RecordType[],
        ) => {
          (emit as any)?.(EventsMap.onSelectAll, selected, selectedRows, changeRows);
        },
        onChange: (keyList: React.Key[]) => {
          setSelectedRowKeys(keyList);
        },
      }
      : undefined;
  }, [selectedRowKeys, rowSelection]);

  const onRow = (record: RecordType): any => {
    return {
      onClick: (event: MouseEvent) => {
        event.stopPropagation();
        tableCacheRef.current.clickRowData = record;
        const key = relationField ?? primaryKey;
        setClickRow(oldKey => {
          if (oldKey !== record[key]) {
            if (childRef.length !== 0) {
              childRef.forEach((ele) => {
                inbiz?.(ele)?.load({
                  ...record,
                  id: record[key],
                  _create: record._create,
                });
              });
            }
            return record[key];
          }
          if (childRef.length !== 0) {
            childRef.forEach((ele) => {
              inbiz?.(ele)?.loadData([]);
            });
          }
          return '';
        });
        (emit as any)?.(EventsMap.onClick, 'tableRowClick', record, event);
      },
      onDoubleClick: (event: MouseEvent) => {
        (emit as any)?.(EventsMap.onDblClickRow, record, event);
      },
    };
  };

  const modalRef = useRef<any>({});
  const importModalRef = useRef<any>({});
  const exportModalRef = useRef<any>({});

  useImperativeHandle(
    ref,
    () => ({
      ...api(eventsRef, {
        modelname: props?.modelname || '',
        searchRef,
        modalFormRef,
        tableCacheRef,
        modalRef,
        selectedRowKeys,
        rest,
        primaryKey,
        pageData,
        setSelectedRowKeys,
        setQueryParam,
        setPageData,
        fetchData,
        dispatch,
        inbiz,
        controlId
      }),
    }),
    [pageData, selectedRowKeys],
  );

  const tablePagination = usePagination({
    total: pageData.total,
    ...searchRef.current.pageInfo,
    pageSizeOptions: Option().pageSize.map((option) => option.value),
    showLessItems: true,
    showQuickJumper: true,
    itemRender: (_, type, originalElement) => {
      if (type === 'next') {
        return (
          <>
            <span>
              <Button
                disabled={nextBtn}
                className="inbiz-pagination-next-btn"
                icon={<RightOutlined />}
              />
            </span>
            {pageRefresh && (
              <span className='inbiz-pagination-refresh'>
                <Button
                  className="inbiz-pagination-refresh-btn"
                  icon={<SyncOutlined />}
                  onClick={(e) => {
                    e && e.stopPropagation();
                    refreshData();
                  }}
                />
              </span>
            )}
          </>
        );
      }
      return originalElement;
    },
    size: 'default',
    ...(pagination || {})
  });

  const handleTableChange = (
    pagination: TablePaginationConfig,
    filters: Record<string, FilterValue | null>,
    sorter: SorterResult<RecordType> | SorterResult<RecordType>[],
    extra: TableCurrentDataSource<RecordType>,
  ) => {
    if (extra.action === 'filter') {
      // 需求调整，暂不添加缓存
      // setTableStorage({
      //   inbiz,
      //   controlId,
      //   filterValues: { ...filters },
      // });
      return setAttrFilters((oldData) => {
        return {
          ...filters,
          inBizTableActions: filters?.[ColumnConst.actions]
            ? filters?.[ColumnConst.actions]
            : oldData?.[ColumnConst.actions],
        };
      });
    }
    let newQueryParam = { ...queryParam };
    if (extra.action === 'paginate') {
      newQueryParam = {
        ...queryParam,
        current: pagination.current!,
        pageSize: pagination.pageSize!,
      };
    } else if (extra.action === 'sort' && sorter) {
      setQuerySortField(undefined);
      let orders = [...(queryParam.orders || [])];
      if (Array.isArray(sorter)) {
        orders = orders.filter((order) => sorter.find((item) => item.field === order.fieldName));
        sorter.forEach((each) => {
          orders = formatters.formatOrders(orders, each, models);
        });
        newQueryParam = { ...queryParam, orders };
      } else {
        orders = orders.filter((order) => sorter.field === order.fieldName);
        newQueryParam = { ...queryParam, orders: formatters.formatOrders(orders, sorter, models) };
      }
    }

    const params = {
      pageInfo: {
        current: newQueryParam.current,
        pageSize: newQueryParam.pageSize,
      },
      iConditions: {
        ...(searchRef.current.iConditions ?? {}),
        pageSize: newQueryParam.pageSize,
        pageIndex: newQueryParam.current,
      },
      orders: newQueryParam.orders,
    };
    const res = fetchData(params);
    searchRef.current = {
      ...searchRef.current,
      ...params,
    };
    Promise.resolve(res).then(() => setQueryParam(newQueryParam));
  };

  const onSortEnd = useCallback(
    async ({ oldIndex, newIndex, collection }) => {
      const iParams =
        (await eventsRef?.current.onBeforeSort?.({
          oldIndex,
          newIndex,
          data: pageData.data,
        })) ?? {};
      if (!iParams) {
        return false;
      }
      if (oldIndex !== newIndex) {
        let result;
        const currentData = pageData?.data?.[oldIndex];
        const referenceData = pageData?.data?.[newIndex];
        const param: {
          sort?: boolean;
          currentId?: string;
          reference?: string;
          position?: 'before' | 'after';
          sortField?: string;
        } = {
          currentId: currentData?.id,
          reference: referenceData?.id,
          position: newIndex == 0 || oldIndex > newIndex ? 'before' : 'after',
          sortField: sortField,
          sort: true,
        };
        if (sortInterface) {
          const request = customRequest(sortInterface as any, inbiz);
          if (sortInterface['url']) {
            result = await request({ ...iParams });
          } else {
            result = await request({
              iConditions: {
                ...iParams,
              },
            });
          }
          fetchData();
        } else {
          fetchData(param);
        }
        (emit as any)?.(EventsMap.onAfterSort, result);
        setPageData((oldPageData): PageData => {
          const newData = arrayMoveImmutable(
            [...(oldPageData?.data || [])],
            oldIndex,
            newIndex,
          ).filter((el) => !!el);
          return {
            data: newData,
            total: oldPageData?.total,
            primary: oldPageData.primary,
          };
        });
      }
      return;
    },
    [sortField, sortInterface, pageData],
  );

  const DraggableContainer = useCallback(
    (bodyProps: SortableContainerProps) => {
      const sortableRef: any = useRef();
      return (
        <SortableBody
          ref={sortableRef}
          pressDelay={pressDelay || 130}
          useDragHandle={showSortIcon}
          helperClass="table-row-dragging"
          helperContainer={() => {
            return sortableRef.current.scrollContainer ||
              document.body
          }}
          onSortEnd={onSortEnd}
          {...bodyProps}
        />
      );
    },
    [onSortEnd, showSortIcon, pressDelay],
  );

  const DraggableBodyRow = useCallback(
    ({ ...restProps }) => {
      const index = pageData.data?.findIndex((x) => x[primaryKey] === restProps['data-row-key']);
      return <SortableItem index={index || 0} {...restProps} />;
    },
    [pageData, primaryKey],
  );


  const DragHandle = useMemo(() => {
    return SortableHandle(() => <MenuOutlined style={{ cursor: 'move', color: '#999' }} />);
  }, []);

  // const formattedTitle = useTitle(title);

  const filteredColumns = useMemo(() => {
    if (!attrFilters || !columns) {
      return columns;
    }
    let cacheColumns: any = columns;
    if (dragSort && showSortIcon) {
      const sortColumn = {
        title: () => {
          return (
            <span title={getMessage('Table.sortField')}>
              <TextWidget>Table.sortField</TextWidget>
            </span>
          );
        },
        dataIndex: 'inBizTableSort',
        width: 50,
        fixed: showPosition ? showPosition : false,
        className: 'drag-visible',
        render: () => <DragHandle />,
      };
      if (viewMode != 'look') {
        if (showPosition == 'left') {
          cacheColumns.unshift(sortColumn);
        } else {
          cacheColumns.push(sortColumn);
        }
      }

    }
    if (attrFilters?.[ColumnConst.actions]) {
      cacheColumns = columns.filter(
        (column: ColumnType<RecordType>) =>
          column.fixed ||
          attrFilters[ColumnConst.actions].includes(column.dataIndex as string) ||
          [ColumnConst.index, ColumnConst.sort, ColumnConst.actions].includes(
            column.dataIndex as string,
          ),
      );
    }
    cacheColumns.forEach((column) => {
      if (column.filter) {
        column.filteredValue = attrFilters?.[column.dataIndex] || column.filteredValue;
      }
      if (querySortField?.fieldName === column.dataIndex) {
        column.sortOrder = querySortField?.sortType === 'asc' ? 'ascend' : 'descend'
      }
    });
    return cacheColumns;
  }, [attrFilters, columns, attrs, dragSort, querySortField, showSortIcon, showPosition, GlobalRegistry.getDesignerLanguage()]);
  const totalWidth = useColumnsWidth([...filteredColumns]);
  const mergedColumns = useColumns(
    tableWidth,
    totalWidth,
    filteredColumns,
    pageData,
    showHeightRule != 'fixed' ? height : tableHeight,
  );
  const scrollX = useScrollX(tableWidth, totalWidth);
  const handleResize = useCallback(
    (index: number) =>
      (e: any, { size }: any) => {
        if (mergedColumns) {
          mergedColumns[index].width = size.width < 50 ? 50 : size.width;
        }
        if (formItems.widthUnit === UnitEnum.percentage) {
          const nextColumns = percentageWidthCount({
            tableWidth,
            rowSelection,
            dragSort,
            columns: mergedColumns,
          });
          nextColumns.forEach((ele, index) => {
            mergedColumns[index].width = ele.width;
          });
        }
        setTableStorage({
          inbiz,
          controlId,
          mergedColumns: mergedColumns.map((item) => {
            return {
              dataIndex: item.dataIndex,
              width: item.width,
            };
          }),
        });
        setResizeColumn((prev) => !prev);
      },
    [mergedColumns],
  );
  if (viewMode != 'look') {
    useResize(mergedColumns, handleResize);

  }

  /**
   * 操作完数据后更新表格
   * @param value 父表格选中行的主键值
   */
  const loadData = (value: string, pageInfo?: any) => {
    if (selectComponent) {
      inbiz?.(controlId).load({
        id: value,
      });
    } else {
      tableCacheRef.current.refresh(pageInfo);
    }
  };

  const onResize = ({ width, offsetHeight }: { width: number; offsetHeight: number }) => {
    window.requestAnimationFrame(() => {
      if (showHeightRule == 'fixed') {
        const _height = height || 0;
        const { maxHeight } = formItems;
        const heights = getHeights({ controlId, maxHeight, showPage });
        const table: HTMLDivElement = inbizTableRef.current.querySelector('.ant-table');
        if (table) {
          const { windowHeight, paginationHeight, tableHeader } = heights;
          const _tableHeight =
            windowHeight - offsetHeight - paginationHeight - _height - boxPadding - boxMargin - 22;
          table.style.height = `${_tableHeight}px`;
          setTableHeight(_tableHeight - tableHeader - 2);
          setTimeout(() => {
            const table: HTMLDivElement = inbizTableRef.current.querySelector('.ant-table');
            table.style.height = `${_tableHeight}px`;
          }, 20)
        }
      }
      if (formItems.widthUnit === UnitEnum.percentage) {
        setTableWidth(width);
      } else {
        const hasActions = mergedColumns.some(
          (columns: any) => columns.dataIndex === 'inBizTableActions',
        );
        if (hasActions) {
          setTableWidth(width - formItems.actionColumnWidth);
        } else {
          setTableWidth(width);
        }
      }
    })
  };

  const setRowClass = (record: any) => {
    // 单击行样式
    if (record[primaryKey] === clickRow) {
      return 'table-clickrow';
    }
    // 多选时，取消选中行背景色样式
    if (!selectBackColor) {
      return 'not-select-row-color';
    }
    return '';
  };

  const [state, dispatch] = useActionReducer({
    handlePrint,
    modalRef,
    tableCacheRef,
    importModalRef,
    exportModalRef,
  });



  const _lang = GlobalRegistry.getDesignerLanguage();
  const _locale = commonLang[_lang?.toLocaleLowerCase()?.replace('-', '_') as keyof typeof commonLang || 'zh_cn'];
  const [locale, $locale] = useState(_locale)

  useEffect(() => {
    $locale(_locale)
  }, [GlobalRegistry.getDesignerLanguage(), _locale]);


  const providerValue = useMemo(() => {
    return {
      state,
      tableRef,
      tableCacheRef,
      selectedRowKeys,
      conditions: searchRef.current?.conditions?.[0],
      dispatch,
      emit,
      fetchTagData,
    };
  }, [state, tableCacheRef, searchRef?.current, selectedRowKeys, dispatch, emit, tagData]);

  const getTableData = useCallback(() => {
    const searchComponents = (inbiz as any)?.getFields?.(controlId).filter(item =>
      item.hasOwnProperty('value') && item?.componentProps?.tablequeryfield && !item.hidden
    );
    // 查询区域有查询组件且绑定了查询字段，需等待查询区域渲染完成后，处理默认值，在获取表格数据
    if (searchComponents.length) {
      let i: number = 0;
      let values: { [key: string]: any } = {};
      searchComponents.forEach((component: Field) => {
        const fieldControlId = component?.componentProps?.controlId;
        inbiz(fieldControlId).on('mount', () => {
          i = i + 1;
          if (searchComponents?.length === i) {
            searchComponents.forEach((field: Field) => {
              values[field?.componentProps?.tablequeryfield] = {
                value:
                  field?.componentType === 'EformMemberSelect'
                    ? field?.value?.map((value: any) =>
                      formatters.formatMemberSelectValue(value, field?.componentProps?.tabs),
                    )
                    : field?.value,
                type: field?.componentType,
                name: field.props.name,
              };
              const initParams = searchRef.current.initParams;
              const searchValues =
                objIsNotEmpty(values) && queryFields
                  ? formatters.getQueryGroups(queryFields, values, initParams)
                  : [];
              searchRef.current = {
                ...searchRef.current,
                pageInfo: {
                  ...searchRef.current.pageInfo!,
                  current: 1,
                },
                conditions: searchValues,
              };
            })
            fetchData();
          }
        })
      })
    } else {
      // 查询区域无查询组件时，直接获取表格数据
      fetchData();
    }
  }, []);

  const getModelQueryInfo = useCallback(async () => {
    if (!props.sourceModel) {
      return
    }
    const res = await tableApi.findModelQuery({ appId: inbiz?.appInfo?.appId, key: props.sourceModel! });
    const { SortConfigs } = res?.ModelQueryConfig || {};
    if (SortConfigs?.length) {
      setQuerySortField({
        fieldName: SortConfigs[0].FieldName,
        sortType: SortConfigs[0].SortType
      })
    }
  }, [])

  useEffect(() => {
    tableCacheRef.current = {
      ...tableCacheRef.current,
      keyList: selectedRowKeys,
      setSelect: (keys: any) => {
        setSelectedRowKeys(keys);
      },
      data: pageData.data,
      total: pageData.total,
      refresh: fetchData,
      loadData,
      primaryKey,
      ...searchRef.current,
    };
  });
  useEffect(() => {
    if (autoLoad && !selectComponent) {
      getTableData();
    } else {
      //@ts-ignore
      const fields = inbiz?.getFields() ?? [];
      if (selectComponent) {
        fields.forEach((ele: any) => {
          if (ele.componentProps.controlId === selectComponent) {
            tableCacheRef.current.parentModel = ele.componentProps.modelname;
          }
        });
      }
    }
    if (props.sourcetype === 'model') {
      getModelQueryInfo();
    }
  }, []);
  useEffect(() => {
    const isDisabled = setTableBodyHeight({ inbizTableRef, tableHeight, pageData, showHeightRule });
    setNextBtn(isDisabled ? JSON.parse(isDisabled) : false);
  }, [pageData.data, tableHeight, showHeightRule, inbizTableRef]);

  return (
    <TableCtx.Provider value={providerValue as TableCtxType}>
      <ResizeObserver onResize={onResize}>
        <ConfigProvider locale={locale}>
          <TableTop searchNode={viewMode != 'look' && <Search fetchData={fetchData} />} viewMode={viewMode} />
          <div
            style={{ display: printVisible ? 'none' : undefined }}
            className={!toolbar?.actions?.length && !search ? 'inbiz-table-not-top' : ''}
          >
            <Table
              rowKey={primaryKey}
              loading={loading}
              //且非固定列宽度之和不要超过 scroll.x
              scroll={{
                x: scrollX,
                y: showHeightRule != 'fixed' ? height : tableHeight,
              }}
              bordered={bordered}
              className={loading ? cls(tablePrefixCls) : ''}
              pagination={showPage ? tablePagination : false}
              columns={mergedColumns}
              dataSource={pageData.data || []}
              components={{
                header: {
                  cell: ResizableTitle,
                },
                body: dragSort ? {
                  wrapper: DraggableContainer,
                  row: DraggableBodyRow,
                } : undefined,
              }}
              rowSelection={viewMode == 'look' ? null : newRowSelection}
              onRow={onRow}
              sticky={true}
              {...rest}
              size={props.rowHeight}
              ref={tableRef}
              rowClassName={setRowClass}
              onChange={handleTableChange}
            />
          </div>
          <PrintTable
            ref={printTableRef}
            style={{ display: printVisible ? 'block' : 'none' }}
            columns={mergedColumns}
            dataSource={pageData.data}
            size={props.rowHeight}
          />
        </ConfigProvider>
      </ResizeObserver>
      <TableModal
        ref={modalRef}
        state={state}
        primaryKey={primaryKey}
        relationField={relationField}
      />
      <ImportAction ref={importModalRef} showBtn={false} />
      <ExportAction ref={exportModalRef} showBtn={false} />
    </TableCtx.Provider>
  );
};

export default forwardRef(InBizTable);
